package com.boarsoft.boar.soagov.flow.action;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletOutputStream;

import org.apache.commons.io.FileUtils;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.boarsoft.boar.common.Constants;
import com.boarsoft.boar.entity.ComInfo;
import com.boarsoft.boar.entity.FlowInfo;
import com.boarsoft.boar.entity.ObjFaces;
import com.boarsoft.boar.entity.SoaNexus;
import com.boarsoft.boar.entity.SvcInfo;
import com.boarsoft.boar.soagov.ObjFacesBiz;
import com.boarsoft.boar.soagov.flow.FlowInfoBiz;
import com.boarsoft.boar.soagov.nexus.NexusBiz;
import com.boarsoft.boar.soagov.svc.SvcBiz;
import com.boarsoft.common.Authorized;
import com.boarsoft.common.Util;
import com.boarsoft.common.action.PageAction;
import com.boarsoft.common.dao.PagedResult;
import com.boarsoft.common.util.Base64;
import com.boarsoft.common.util.BeanUtil;
import com.boarsoft.common.util.FileUtil;
import com.boarsoft.common.util.JsonUtil;
import com.boarsoft.flow.store.SimpleFlowStore;

/**
 * 流程图服务
 *
 * @author Mac_J
 */
@SuppressWarnings("serial")
@Component
@Scope("prototype")
@ParentPackage("default-package")
@Namespace(value = "/flow")
public class FlowInfoAction extends PageAction {
	@Autowired
	private NexusBiz nexusBiz;
	@Autowired
	private ObjFacesBiz objFacesBiz;
	@Autowired
	private FlowInfoBiz flowInfoBiz;
	@Autowired
	private SimpleFlowStore flowStore;
	@Autowired
	private SvcBiz svcBiz;

	/** 服务ID */
	private String id;
	/** 服务编号 */
	private String code;
	/** 服务名称 */
	private String name;
	/** 接口ID */
	private String faceId;
	/** 实现类 */
	private String impl;
	/** 实现类的版本 */
	private String ver;
	/** 所属系统 */
	private String catalog;
	/** 是普通服务还是有流程的服务 */
	private short type = FlowInfo.TYPE_FLOW;
	/** 访问路径 */
	private String url;
	/** 导出路径 */
	private String path;
	/** 服务描述 */
	private String memo;
	/** 访问路径 */
	private String thumb;
	/** 标识当前编辑的只是流程，非服务 */
	private boolean flowOnly = false;
	private String nexus; // flow所引用的所有组件
	private String faces; // flow所涉及的接口
	private String json;

	@SuppressWarnings("unchecked")
	@Action("save")
	@Authorized(code = "flow.save")
	public void save() {
		// 更新或保存对象
		FlowInfo fo = flowInfoBiz.get(id);
		if (fo == null) {
			fo = new FlowInfo();
			fo.setId(id);
			fo.setCode(code);
			fo.setName(name);

			fo.setStatus(FlowInfo.STATUS_DRAFT);
			fo.setCatalog(catalog);
			fo.setType(type);
			fo.setUrl(url);
		}
		fo.setFaceId(faceId);
		fo.setImpl(impl);
		fo.setVer(ver);
		flowInfoBiz.save(fo);

		// 保存与组件的关系（先删除，再添加）
		List<SoaNexus> nLt = new ArrayList<SoaNexus>();
		log.info(nexus);
		// List<Map<String, Object>> mLt = new Gson().fromJson(nexus,
		// ArrayList.class);
		List<Map<String, Object>> mLt = JsonUtil.parseObject(nexus, ArrayList.class);
		SoaNexus z = null;
		for (Map<String, Object> m : mLt) {
			z = new SoaNexus();
			BeanUtil.fillObjectWithMap(z, m);
			log.info(JsonUtil.from(z));
			nLt.add(z);
		}
		// 保存与接口的关系
		String[] fa = faces.split(",");
		for (String faceId : fa) {
			ObjFaces n = new ObjFaces();
			n.setObjId(id);
			n.setObjType(ComInfo.TYPE_SUBFLOW);
			n.setFaceId(faceId);
			objFacesBiz.save(n);// 此方法会检查objId0和objId1的依赖是否存在
		}
		// 保存图表（此路径与导出路径不同）
		// DirInfo d = dirBiz.get(id);// flowId与dirId相同
		// StringBuilder sb = new StringBuilder();
		// sb.setLength(0);
		// sb.append(Config.PATH_PRIVATE).append(d.getPath());
		// sb.append("/").append(d.getCode()).append("/diagram.js");
		// sb.append(Config.PATH_PRIVATE).append("/").append(id).append("/diagram.js");
		// String pv = sb.toString();
		String pv = flowStore.getDiagramPath(id).concat("/diagram.js");
		log.info("Save flow diagram json to {}", pv);
		try {
			File fj = new File(pv);
			FileUtil.makePath(fj.getParent());
			FileUtil.write(fj, json);
		} catch (Exception e) {
			log.error("Error on save flow diagram json to {}", pv, e);
			writeReply(Constants.ERROR_UNKNOWN, "Msg.flow.save");
			return;
		}
		// sb.setLength(0);
		// sb.append(Config.PATH_PRIVATE).append(d.getPath());
		// sb.append("/").append(d.getCode()).append("/diagram.png");
		// sb.append(Config.PATH_PRIVATE).append("/").append(id).append("/diagram.png");
		// String pb = sb.toString();
		String pb = flowStore.getDiagramPath(id).concat("/diagram.png");
		String[] pd = thumb.split(",");
		log.info("Save flow diagram thumb to {}", pd, pb);
		try {
			File fp = new File(pb);
			FileUtil.makePath(fp.getParent());
			FileUtils.writeByteArrayToFile(fp, Base64.decode(pd[1]));
			writeReply();
		} catch (Exception e) {
			log.error("Error on save flow diagram thumb to {}", pb, e);
			writeReply(Constants.ERROR_UNKNOWN, "Msg.flow.save");
			return;
		}
	}

	@Action("load")
	@Authorized(code = "flow.load")
	public void load() {
		// DirInfo d = dirBiz.get(id);// flowId与dirId相同
		// if (d == null) {
		// writeTextReply(Constants.ERROR_NOTFOUND);
		// return;
		// }

		String pv = flowStore.getDiagramPath(id).concat("/diagram.js");
		File f = new File(pv);
		log.info("Load flow diagram json from {}", pv);
		try {
			// File f = new File(pv);
			if (f.exists()) {
				String json = FileUtil.read(f);
				writeReply(true, json);
			} else {
				writeReply();
			}
		} catch (Exception e) {
			log.error("Error on load flow diagram json from {}", pv, e);
			writeReply(Constants.ERROR_UNKNOWN, "Msg.flow.load");
		}
	}

	@Action(value = "thumb")
	@Authorized(code = "flow.thumb")
	public void thumb() {
		// DirInfo d = dirBiz.get(id);// flowId与dirId相同
		// StringBuilder sb = new StringBuilder();
		// sb.append(Config.PATH_PRIVATE).append(d.getPath());
		// sb.append("/").append(d.getCode()).append("/diagram.png");
		// String pv = sb.toString();
		String pv = flowStore.getDiagramPath(id).concat("/diagram.png");
		log.info("Load flow diagram thumb from {}", pv);
		try {
			File f = new File(pv);
			if (f.exists()) {
				byte[] ba = FileUtils.readFileToByteArray(f);
				if (Util.strIsEmpty(url)) {
					response.setContentType("image/png");
					ServletOutputStream os = response.getOutputStream();
					os.write(ba);
					response.flushBuffer();
				} else {
					writeTextReply(true, "data:image/png;base64,".concat(Base64.encode(ba)));
				}
			} else {
				writeReply(Constants.ERROR_NOTFOUND, "Msg.flow.diagram");
			}
		} catch (Exception e) {
			try {
				response.getOutputStream().close();
			} catch (IOException e1) {
			}
			log.error("Error on load flow diagram thumb from {}", pv, e);
			// writeReply(Constants.ERROR_UNKNOWN);
			writeReply(Constants.ERROR_UNKNOWN, "Msg.flow.diagram");
		}
	}

	@Action("edit")
	// @Authorized(code = "flow.edit")
	public void edit() {
		if (Util.strIsEmpty(id) || Util.strIsEmpty(code) || Util.strIsEmpty(url)) {
			writeReply(Constants.ERROR_INVALID, "Msg.common.param");
			return;
		}
		FlowInfo fo = flowInfoBiz.get(id);
		if (fo == null) {
			fo = new FlowInfo();
			fo.setId(id);
			fo.setCode(code);
			fo.setName(name);
			fo.setFaceId(faceId);
			fo.setImpl(impl);
			fo.setVer(ver);
			fo.setStatus(FlowInfo.STATUS_DRAFT);
			fo.setCatalog(catalog);
			fo.setType(type);
			fo.setUrl(url);
			fo.setPath(flowStore.getXmlPath(id));
			fo.setPath(path);
			fo.setMemo(memo);
			flowInfoBiz.save(fo);
		}

		if (!flowOnly) {
			SvcInfo so = svcBiz.get(id);
			if (so == null) {
				so = new SvcInfo();
				so.setId(id);
				so.setCode(code);
				so.setName(name);
				so.setFaceId(faceId);
				so.setImpl(impl);
				so.setVer(ver);
				so.setStatus(FlowInfo.STATUS_DRAFT);
				so.setType(type);
				so.setUrl(url);
				so.setPath(flowStore.getXmlPath(id));
				so.setPath(path);
				so.setMemo(memo);
				svcBiz.save(so);
			}
		}
		try {
			response.sendRedirect(url);
		} catch (IOException e) {
			log.error("Can not redirect to {}", url);
			writeReply(Constants.ERROR_INVALID, "Msg.flow.url");
		}
	}

	@Action("get")
	// @Authorized(code = "flow.get")
	public void get() {
		if (!this.isValid("id"))
			return;
		//
		FlowInfo fo = flowInfoBiz.get(id);
		if (fo == null) {
			writeReply(Constants.ERROR_NOTFOUND, "Msg.common.flow");
			return;
		}
		StringBuilder sb = new StringBuilder();
		sb.append("{ \"success\": true, \"data\": ");
		sb.append(JsonUtil.from(fo)).append(", \"byMe\": { \"0\": {}");
		List<SoaNexus> byLt = nexusBiz.listByMe(id);
		for (SoaNexus n : byLt) {
			sb.append(", \"").append(n.getObjId1()).append("\": ");
			sb.append(JsonUtil.from(n, "face,obj0,lib"));
		}
		sb.append(" }, \"onMe\": { \"0\": {}");
		List<SoaNexus> onLt = nexusBiz.listOnMe(id);
		for (SoaNexus n : onLt) {
			sb.append(", \"").append(n.getObjId1()).append("\": ");
			sb.append(JsonUtil.from(n, "face,obj1,lib"));
		}
		sb.append(" }, \"faces\": { \"0\": {}");
		PagedResult<ObjFaces> cfPr = objFacesBiz.list(id, "", "", "", "", 1, 9999);
		for (ObjFaces n : cfPr.getList()) {
			sb.append(", \"").append(n.getFaceId()).append("\": ");
			sb.append(JsonUtil.from(n.getFace()));
		}
		sb.append(" } }");
		write(sb.toString());
	}

	/**
	 * 查询最近编辑的服务
	 */
	@Action("find")
	@Authorized(code = "flow.find")
	public void find() {
		PagedResult<FlowInfo> pr = flowInfoBiz.find(pageNo, pageSize);
		writeReply(true, JsonUtil.from(pr, "face"));
	}

	public NexusBiz getNexusBiz() {
		return nexusBiz;
	}

	public void setNexusBiz(NexusBiz nexusBiz) {
		this.nexusBiz = nexusBiz;
	}

	public ObjFacesBiz getObjFacesBiz() {
		return objFacesBiz;
	}

	public void setObjFacesBiz(ObjFacesBiz objFacesBiz) {
		this.objFacesBiz = objFacesBiz;
	}

	public FlowInfoBiz getFlowInfoBiz() {
		return flowInfoBiz;
	}

	public void setFlowInfoBiz(FlowInfoBiz flowInfoBiz) {
		this.flowInfoBiz = flowInfoBiz;
	}

	public SimpleFlowStore getFlowStore() {
		return flowStore;
	}

	public void setFlowStore(SimpleFlowStore flowStore) {
		this.flowStore = flowStore;
	}

	public SvcBiz getSvcBiz() {
		return svcBiz;
	}

	public void setSvcBiz(SvcBiz svcBiz) {
		this.svcBiz = svcBiz;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getFaceId() {
		return faceId;
	}

	public void setFaceId(String faceId) {
		this.faceId = faceId;
	}

	public String getImpl() {
		return impl;
	}

	public void setImpl(String impl) {
		this.impl = impl;
	}

	public String getVer() {
		return ver;
	}

	public void setVer(String ver) {
		this.ver = ver;
	}

	public String getCatalog() {
		return catalog;
	}

	public void setCatalog(String catalog) {
		this.catalog = catalog;
	}

	public short getType() {
		return type;
	}

	public void setType(short type) {
		this.type = type;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}

	public String getMemo() {
		return memo;
	}

	public void setMemo(String memo) {
		this.memo = memo;
	}

	public String getThumb() {
		return thumb;
	}

	public void setThumb(String thumb) {
		this.thumb = thumb;
	}

	public boolean isFlowOnly() {
		return flowOnly;
	}

	public void setFlowOnly(boolean flowOnly) {
		this.flowOnly = flowOnly;
	}

	public String getNexus() {
		return nexus;
	}

	public void setNexus(String nexus) {
		this.nexus = nexus;
	}

	public String getFaces() {
		return faces;
	}

	public void setFaces(String faces) {
		this.faces = faces;
	}

	public String getJson() {
		return json;
	}

	public void setJson(String json) {
		this.json = json;
	}
}